package vg.modules.search;


import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridLayout;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

import vg.modules.search.components.SecondPanel;
import vg.modules.search.components.firstPanel.FirstPanel;
import vg.modules.search.components.firstPanel.FirstPanelData;
import vg.services.graph_view_manager.interfaces.graph_view.AGraphView;
import vg.services.main_manager.MainManager;
import vg.services.plugin_manager.realization.PluginParameter;

/**
 * This class implements ISearchPanel interface.
 * @author tzolotuhin
 */
public class SearchPanel extends JFrame implements FBSearchPanel {
	private static final long serialVersionUID = 1L;

	// Swing components
	private JPanel view;
	private FirstPanel firstPanel;
	private SecondPanel secondPanel;
	private JButton searchButton, cancelButton, backButton;

	// Main data
	private boolean init = false;
	private boolean close;
	private Map<Integer, String> graphs;
	private AGraphView current_view;
	private final PluginParameter param;

	// Mutex
	private final Object theMutexObject = new Object(); 

	// Window settings
	private final static String DEF_WINDOWS_SIZE_X = "SEARCH_PLUGIN_WindowSizeX";
	private final static String DEF_WINDOWS_SIZE_Y = "SEARCH_PLUGIN_WindowSizeY";
	private final static String DEF_WINDOWS_POS_X = "SEARCH_PLUGIN_WindowPosX";
	private final static String DEF_WINDOWS_POS_Y = "SEARCH_PLUGIN_WindowPosY";
	private Integer windowSizeX, windowSizeY;
	private Integer windowPosX, windowPosY;

    /**
	 * Constructor.
	 * @param title - title of frame.
	 * @param parameter - connection with model and user interface.
	 */
	public SearchPanel(final String title, final PluginParameter parameter) {
		super(title);
		// init main data
		this.param = parameter;
		this.graphs = new HashMap<Integer, String>(10);
	}
	/**
	 * This method adds new graph to model.
	 * @param id - id of graph in data base.
	 * @param name - name of graph.
	 */
	public void addGraph(final int id, final String name) {
		synchronized (theMutexObject) {
			this.graphs.put(id, name);
			if(init) {
				if(SwingUtilities.isEventDispatchThread()) {
					this.firstPanel.addGraph(id, name);
				} else {
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							firstPanel.addGraph(id, name);
						}
					});
				}
			}
		}
	}
	
	public void setCurrentView(final AGraphView currentView) {
		synchronized (theMutexObject) {
			current_view = currentView;
			if(init) {
				if(SwingUtilities.isEventDispatchThread()) {
					firstPanel.setCurrentView(currentView);
				} else {
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							synchronized (theMutexObject) {
								firstPanel.setCurrentView(currentView);
							}
						}
					});
				}
			}
		}
	}
	
	/**
	 * This method opens search plugin.
	 */
	public void open() {
		synchronized (theMutexObject) {
			if(SwingUtilities.isEventDispatchThread()) {
				if(!init) {
					init();
				}
				setClose(false);
			} else {
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						synchronized (theMutexObject) {
							if(!init) {
								init();
							}
							setClose(false);							
						}
					}
				});
			}
		}
	}
	
	/**
	 * This method closes search plugin.
	 */
	public void close() {
		synchronized (theMutexObject) {
			if(SwingUtilities.isEventDispatchThread()) {
				setClose(true);
			} else {
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						synchronized (theMutexObject) {
							setClose(true);							
						}
					}
				});
			}
		}
	}
	
	/**
	 * This method updates ui theme.
	 */
	public void updateUITheme() {
		synchronized (theMutexObject) {
			if(init) {
				if(SwingUtilities.isEventDispatchThread()) {
					SwingUtilities.updateComponentTreeUI(SearchPanel.this);
					SwingUtilities.updateComponentTreeUI(SearchPanel.this.backButton);
					SwingUtilities.updateComponentTreeUI(SearchPanel.this.searchButton);
					SwingUtilities.updateComponentTreeUI(SearchPanel.this.cancelButton);
					firstPanel.updateUITheme();
					secondPanel.updateUITheme();					
				} else {
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							synchronized (theMutexObject) {
								SwingUtilities.updateComponentTreeUI(SearchPanel.this);
								SwingUtilities.updateComponentTreeUI(SearchPanel.this.backButton);
								SwingUtilities.updateComponentTreeUI(SearchPanel.this.searchButton);
								SwingUtilities.updateComponentTreeUI(SearchPanel.this.cancelButton);
								firstPanel.updateUITheme();
								secondPanel.updateUITheme();
							}
						}
					});
				}
			}
		}
	}

	public void reset() {
		synchronized (theMutexObject) {
			if(!init) return;
			this.firstPanel.reset();
		}
	}
//=============================================================================
//------------PRIVATE METHODS--------------------------------------------------
	/**
	 * This method realizes action for search button. 
	 */
	private void search() {
		FirstPanelData data = firstPanel.getFirstPanelData();
		// search vertices
		Set<Integer> local_search_result = DataBaseWorker4Search.get_vertex_ids(data.conditionTokens, param.graphDataBase, data.graphModelId);
		
		Set<Integer> search_result = new HashSet<Integer>();
		if (local_search_result != null) {
			for(Integer buf : local_search_result) {
				if(data.vertexIds.contains(buf))
					search_result.add(buf);
			}
		}
		
		secondPanel.buildTree(param.graphDataBase.getGraphModelTree(data.graphModelId), search_result, data.attributes);
		
		buildSecondPanel();		
	}
	/**
	 * This method returns to first page.
	 */
	private void back() {
		buildFirstPanel();
	}
	
	private void setClose(boolean state) {
		this.close = state;
		if(!close) {
			// open search panel
			buildFirstPanel();
			setVisible(true);
		} else {
			// close search panel
			setVisible(false);
		}
	}
	
	private void buildFirstPanel() {
		this.view.removeAll();
		
		GridBagConstraints gbc = new GridBagConstraints(0,0,  2,1,  1,1,  GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5),  0,0);
		this.view.add(this.firstPanel.getView(), gbc);
		
		gbc = new GridBagConstraints(0,1,  1,1,  1,0,  GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0);
		this.view.add(this.cancelButton, gbc);
		
		gbc = new GridBagConstraints(1,1,  1,1,  0,0,  GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 5, 5, 5),  0,0);
		this.view.add(this.searchButton, gbc);
		
		this.view.updateUI();
	}
	
	private void buildSecondPanel() {
		this.view.removeAll();
		
		GridBagConstraints gbc = new GridBagConstraints(0,0,  1,1,  1,1,  GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5),  0,0);
		this.view.add(this.secondPanel.getView(), gbc);
		
		gbc = new GridBagConstraints(0,1,  1,1,  1,0,  GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5),  0,0);
		this.view.add(this.backButton, gbc);

		this.view.updateUI();		
	}
	
	private void init() {
		this.init = true;
		this.view = new JPanel(new GridBagLayout());
		// Creating of window
		this.loadWindowOptions();
		this.setSize(this.windowSizeX, this.windowSizeY);
		this.setLocation(this.windowPosX, this.windowPosY);
		this.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
		this.setLayout(new GridLayout(1, 1));
		this.addComponentListener(new ComponentAdapter() {
			public void componentResized(ComponentEvent e) {
				super.componentResized(e);
			    Component c = (Component)e.getSource();
		        Integer sizeX = c.getSize().width;
		        Integer sizeY = c.getSize().height;
		        MainManager.config.setProperty(DEF_WINDOWS_SIZE_X, sizeX.toString());
		        MainManager.config.setProperty(DEF_WINDOWS_SIZE_Y, sizeY.toString());
			}
			public void componentMoved(ComponentEvent e) {
				super.componentMoved(e);
			    Component c = (Component)e.getSource();
		        Integer posX = c.getLocation().x;
		        Integer posY = c.getLocation().y;
		        MainManager.config.setProperty(DEF_WINDOWS_POS_X, posX.toString());
		        MainManager.config.setProperty(DEF_WINDOWS_POS_Y, posY.toString());
			}
		});
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				super.windowClosed(e);
				close();
			}
		});
		this.cancelButton = new JButton("Cancel");
		this.cancelButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				close();
			}
		});
		this.searchButton = new JButton("Search");
		this.searchButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				synchronized (theMutexObject) {
					search();
				}
			}
		});
		this.backButton = new JButton("Back");
		this.backButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				synchronized (theMutexObject) {
					back();
				}
			}
		});
		//create first and second panels
		firstPanel = new FirstPanel(this.param, this);
		firstPanel.setCurrentView(current_view);
		
		secondPanel = new SecondPanel(this.param, this);
		this.add(view);
		this.buildFirstPanel();
		this.setVisible(false);
		view.updateUI();
		// load current graphs 
		for(Integer buf : graphs.keySet()) {
			this.firstPanel.addGraph(buf, graphs.get(buf));
		}
	}
	/**
	 * Load window options.
	 */
	private void loadWindowOptions() {
		String wSizeStrX = MainManager.config.getProperty(DEF_WINDOWS_SIZE_X);
		String wSizeStrY = MainManager.config.getProperty(DEF_WINDOWS_SIZE_Y);
		String wPosStrX = MainManager.config.getProperty(DEF_WINDOWS_POS_X);
		String wPosStrY = MainManager.config.getProperty(DEF_WINDOWS_POS_Y);
		//set size X(width of window)
		if(wSizeStrX == null) {
			this.windowSizeX = 800;
			MainManager.config.setProperty(DEF_WINDOWS_SIZE_X, this.windowSizeX.toString());
		} else {
			try {
				this.windowSizeX = new Integer(wSizeStrX);
			} catch(NumberFormatException ex) {
				this.windowSizeX = 800;
				MainManager.config.setProperty(DEF_WINDOWS_SIZE_X, this.windowSizeX.toString());
			}
		}
		//set size Y(height of window)
		if(wSizeStrY == null) {
			this.windowSizeY = 600;
			MainManager.config.setProperty(DEF_WINDOWS_SIZE_Y, this.windowSizeY.toString());			
		} else {
			try {
				this.windowSizeY = new Integer(wSizeStrY);
			} catch(NumberFormatException ex) {
				this.windowSizeY = 600;
				MainManager.config.setProperty(DEF_WINDOWS_SIZE_Y, this.windowSizeY.toString());
			}			
		}
		//set position X
		if(wPosStrX == null) {
			this.windowPosX = 200;
			MainManager.config.setProperty(DEF_WINDOWS_POS_X, this.windowPosX.toString());
		} else {
			try {
				this.windowPosX = new Integer(wPosStrX);
			} catch(NumberFormatException ex) {
				this.windowPosX = 800;
				MainManager.config.setProperty(DEF_WINDOWS_POS_X, this.windowPosX.toString());
			}
		}
		//set position Y
		if(wPosStrY == null) {
			this.windowPosY = 200;
			MainManager.config.setProperty(DEF_WINDOWS_POS_Y, this.windowPosY.toString());
		} else {
			try {
				this.windowPosY = new Integer(wPosStrY);
			} catch(NumberFormatException ex) {
				this.windowPosY = 800;
				MainManager.config.setProperty(DEF_WINDOWS_POS_Y, this.windowPosY.toString());
			}
		}
	} 
}